package cheme.ProcessEditor.Parts.Streams;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.draw2d.AbsoluteBendpoint;
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.PolylineConnection;
import org.eclipse.draw2d.PositionConstants;
import org.eclipse.draw2d.RotatableDecoration;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.Request;
import org.eclipse.gef.commands.Command;

import cheme.ProcessEditor.ChemeConstants;
import cheme.ProcessEditor.Figures.DecoratableFigure;
import cheme.ProcessEditor.Figures.StreamFig;
import cheme.ProcessEditor.Figures.TagFig;
import cheme.ProcessEditor.Figures.Util.CalloutLineLocator;
import cheme.ProcessEditor.Figures.Util.ChemePolyLine;
import cheme.ProcessEditor.Figures.Util.TagLocator;
import cheme.ProcessEditor.Parts.Streams.Tag.PartTag;
import cheme.ProcessModel.IPropertyAware;
import cheme.ProcessModel.ProcessUnits.Streams.Stream;

public class AbstractStreamPart extends ChemeAbstractConnectionPart implements PropertyChangeListener
{
	private static final String typeLabel = "Connection";
	private static int instanceCount = 0;
	private String id = "";
	private IFigure descFig = null;
	private PartTag tag = null;
	
	private CalloutLineLocator calloutLineLocator;
	private ChemePolyLine callout;
	
	private RotatableDecoration sourceDecoration = null;
	private RotatableDecoration targetDecoration = null;

	public AbstractStreamPart()
	{
		setId(getNewId());
	}
	
	public RotatableDecoration getSourceDecoration()
	{
		return sourceDecoration;
	}

	public void setSourceDecoration(RotatableDecoration sourceDecoration)
	{
		this.sourceDecoration = sourceDecoration;
	}
	public RotatableDecoration getTargetDecoration()
	{
		return targetDecoration;
	}

	public void setTargetDecoration(RotatableDecoration targetDecoration)
	{
		this.targetDecoration = targetDecoration;
	}



	@Override
	protected void createEditPolicies()
	{
		installEditPolicy(EditPolicy.CONNECTION_ROLE, new ChemeConnectionPolicy());
		installEditPolicy(EditPolicy.CONNECTION_ENDPOINTS_ROLE, new ChemeStreamEndpointPolicy());
		installEditPolicy(EditPolicy.CONNECTION_BENDPOINTS_ROLE, new ChemeBendpointPolicy());
		
		installEditPolicy("Tag Move Role", new TagMoveEditPolicy());
	}
	
	@Override
	public Command getCommand(Request request)
	{
		Command command = null;
		EditPolicyIterator i = getEditPolicyIterator();
		while(i.hasNext())
		{
			if(command != null)
			{
				command = command.chain(i.next().
												 getCommand(request));
			}
			else
			{
				command = i.next().
								   getCommand(request);
			}
		}
		return command;
	}
	
	public String getId()
	{
		return id;
	}

	public void setId(String id)
	{
		this.id = id;
	}

	private String getNewId()
	{
		return typeLabel + " " + new Integer(instanceCount++).toString();
	}
	
	public Stream getModel()
	{
		return (Stream)super.getModel();
	}
	
	public StreamFig getFigure()
	{
		return (StreamFig)super.getFigure();
	}

	protected IFigure createFigure()
	{
		return new StreamFig();
	}
	
	@Override
	protected void removeChild(EditPart child)
	{
		if(!(child instanceof PartTag))
		{
			super.removeChild(child);			
		}
	}

	protected void addTagFig(IFigure child)
	{
		descFig = (DecoratableFigure)child;

		tag.setLocator(new TagLocator(getFigure()));
		tag.getLocator().setRelativePosition(PositionConstants.SOUTH);

		IFigure pane = getContentPane();
		pane.add(descFig, tag.getLocator(), -1);
		
//		IFigure layer = getLayer(ChemeConstants.CHEME_TAG_LAYER);
//		layer.add(descFig, tag.getLocator(), -1);
		
		callout = new ChemePolyLine();
		CalloutLineLocator calloutLineLocator = new CalloutLineLocator(getFigure(), child);
		pane.add(callout, calloutLineLocator, -1);
	}
	
	protected void removeTagFig(IFigure child)
	{
		getContentPane().remove(child);
		
//		IFigure layer = getLayer(ChemeConstants.CHEME_TAG_LAYER);
//		layer.remove(child);
		
		getContentPane().remove(callout);
	}

	/**
	 * Overrides the base addChildVisual(...) such that when a Figure is added to
	 * this Stream (APolyline derivative) it adds a locator to keep the child 
	 * in a preferred orientation with respect to the stream figure.
	 */
	@Override
	protected void addChildVisual(EditPart childEditPart, int index)
	{
		IFigure child = ((GraphicalEditPart)childEditPart).getFigure();
				
		if(null != child && childEditPart instanceof PartTag)
		{
			tag = (PartTag)childEditPart;
			
			addTagFig(child);
			
//			descFig = (DecoratableFigure)child;
//			IFigure layer = getLayer(ChemeConstants.CHEME_SCALED_LAYER);
//			
//			ConnectionLocator locator = new ConnectionLocator(getFigure(), ConnectionLocator.MIDDLE);
//			locator.setRelativePosition(PositionConstants.CENTER);
//			
//			layer.add(descFig, locator, index);
		}
		else
		{
			super.addChildVisual(childEditPart, index);
		}
	}
	
	@Override
	protected void removeChildVisual(EditPart childEditPart)
	{
		IFigure child = ((GraphicalEditPart)childEditPart).getFigure();
		
		if(null != child && childEditPart instanceof PartTag)
		{
			descFig = (TagFig)child;
			removeTagFig(descFig);
			
//			IFigure layer = getLayer(ChemeConstants.CHEME_SCALED_LAYER);
//			layer.remove(descFig);
		}
		else
		{
			super.removeChildVisual(childEditPart);
		}
	}
	
	@Override
	protected void deactivateFigure()
	{
		if(null != descFig)
		{
			removeTagFig(descFig);
//			IFigure layer = getLayer(ChemeConstants.CHEME_SCALED_LAYER);
//			layer.remove(descFig);
		}
		
		super.deactivateFigure();
	}
		
	@Override
	protected List getModelChildren()
	{
		return getModel().getChildren();
	}
		
	@Override
	public void setParent(EditPart parent)
	{
		super.setParent(parent);
	}

	protected void refreshBendpoints()
	{
		List<Point> bendpoints = getModel().getBendpoints();
		List<Point> constraint = new ArrayList<Point>();

		if(null != bendpoints)
		{
			for(Point point : bendpoints)
			{
				constraint.add(new AbsoluteBendpoint(point));
			}
		}
		
		getConnectionFigure().setRoutingConstraint(constraint);
	}

	protected void refreshVisuals()
	{
		refreshBendpoints();
		
		PolylineConnection connection = (PolylineConnection)getFigure();
		
		if(getModel().isTerminalStream())
		{
			/*
			 * Terminal units do not have incoming streams decorated with arrows
			 * or any other target decorator 
			 */
			connection.setTargetDecoration(null);
			connection.setForegroundColor(ColorConstants.lightGray);
		}
		else
		{
			/*
			 * Process units have incoming streams decorated if one is set for the object.
			 */
			connection.setTargetDecoration(getTargetDecoration());
			connection.setForegroundColor(ColorConstants.black);
		}
	}
	
	/*--------------------------------------------------------------------------
	 * PropertyChangeListener
	 */
	/**
	 * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
	 */
	public void propertyChange(PropertyChangeEvent evt)
	{
		try
		{
			String property = evt.getPropertyName();

			if(IPropertyAware.P_BEND_POINT.equals(property))
			{
				handleBendPointChange(evt);
			}
			else if(IPropertyAware.P_FEEDBACK_STATE.equals(property))
			{
				refreshVisuals();
			}
			else
			{
				super.propertyChange(evt);
			}
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}
	
	/**
	 * handles change in bpoints of a stream descriptor, may be overridden by subclass
	 */
	protected void handleBendPointChange(PropertyChangeEvent evt)
	{
		refreshBendpoints();
	}
}
